package com.sondertara.joya.core;

import com.sondertara.common.model.PageResult;
import com.sondertara.joya.core.jdbc.resultset.mapper.BeanRowMapper;
import com.sondertara.joya.core.jdbc.resultset.mapper.ListRecordExtractor;
import com.sondertara.joya.core.jdbc.resultset.mapper.MapRowMapper;
import com.sondertara.joya.core.jdbc.resultset.mapper.ResultSetExtractor;
import com.sondertara.joya.core.jdbc.resultset.mapper.RowMapper;
import com.sondertara.joya.core.jdbc.resultset.mapper.SingleColumnRowMapper;
import com.sondertara.joya.core.jdbc.resultset.mapper.SingleRowRecordExtractor;
import com.sondertara.joya.core.jdbc.statment.BatchPreparedStatementSetter;
import com.sondertara.joya.core.model.PersistEntity;
import com.sondertara.joya.core.query.NativeSqlQuery;
import com.sondertara.joya.core.query.criterion.WhereCondition;
import com.sondertara.joya.core.query.pagination.PageParam;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;

import java.io.Reader;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Collection;
import java.util.List;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;

/**
 * the jdbc operations
 *
 * @author huangxiaohu
 */
public interface JoyaOperations {

    Connection startTransaction();

    void commit();

    void rollback();

    void close(Connection conn);

    /**
     * get direct connection form datasource
     *
     * @return
     */
    Connection getConnection();

    /**
     * run sql script
     *
     * @param reader
     */
    void runScript(@NonNull Reader reader);


    /* ----------------------------------------------------------------select---------------------------------------------------------------- */

    /**
     * 查询数据库并转换结果集。
     * 用户可自定义结果集转换器。
     * 用户也可使用预定义的结果集转换器。
     *
     * @param sql          sql语句
     * @param recordMapper 结果集转换器
     * @param params       sql参数
     * @param <T>          resultSetMapper返回的结果类型
     * @return 成功则返回转换结果，失败则抛出DbException，结果为空则返回空列表
     * @see ResultSetExtractor
     * @see ListRecordExtractor
     * @see SingleRowRecordExtractor
     */
    public <T> T query(String sql, ResultSetExtractor<T> recordMapper, Object... params);

    /**
     * query stream
     *
     * @param sql       the sql
     * @param rowMapper row mapper
     * @param params    the query params
     * @param <T>       the return type
     * @return the stream
     */
    public <T> Stream<T> queryStream(String sql, RowMapper<T> rowMapper, Object... params);
    /**
     * 查询数据库，将结果集的每一行转换成JavaBean，然后将所有行封装成列表。
     *
     * @param sql    sql语句
     * @param type   JavaBean类型
     * @param params sql参数
     * @param <T>    JavaBean类型
     * @return 成功则返回结果列表，失败则抛出DbException，结果为空则返回空列表
     */
    public <T> List<T> queryList(String sql, Class<T> type, Object... params) ;

    /**
     * 查询数据库，返回结果集中的单个值。
     * 如果结果集中有多个值，则只返回第一行第一列的值。
     *
     * @param sql    sql语句
     * @param params sql参数
     * @return 成功则返回结果值，失败则抛出DbException，结果为空则返回null
     */
    public <T> T querySingleValue(String sql, Class<T> clazz, Object... params) ;

    /**
     * 查询数据库，返回结果集中的单行数据。
     * 如果结果集中有多行数据，则只返回第一行数据。
     * 用户可自定义行转换器。
     * 用户也可使用预定义的行转换器。
     *
     * @param sql       sql语句
     * @param rowMapper 行转换器
     * @param params    sql参数
     * @param <T>       rowMapper返回的结果类型
     * @return 成功则返回结果，失败则抛出DbException，结果为空则返回null
     * @see RowMapper
     * @see BeanRowMapper
     * @see MapRowMapper
     * @see SingleColumnRowMapper
     */
    public <T> T querySingleRow(String sql, RowMapper<T> rowMapper, Object... params) ;

    /**
     * 查询数据库，将结果集中的单行数据转换成JavaBean。
     *
     * @param sql    sql语句
     * @param type   JavaBean类型
     * @param params sql参数
     * @param <T>    JavaBean类型
     * @return 成功则返回结果，失败则抛出DbException，结果为空则返回null
     */
    public <T> T querySingleRow(String sql, Class<T> type, Object... params) ;

    /**
     * 查询数据库，对结果集的每一行进行转换，然后将所有行封装成列表。
     * 用户可自定义行转换器。
     * 用户也可使用预定义的行转换器。
     *
     * @param sql       sql语句
     * @param rowMapper 行转换器
     * @param params    sql参数
     * @param <T>       rowMapper返回的结果类型
     * @return 成功则返回结果列表，失败则抛出DbException，结果为空则返回空列表
     * @see BeanRowMapper
     * @see MapRowMapper
     * @see SingleColumnRowMapper
     */
    public <T> List<T> queryList(String sql, RowMapper<T> rowMapper, Object... params) ;

    <T> List<T> queryTop(String sql, Integer top, RowMapper<T> rowMapper, Object... params);

    <T> List<T> queryTop(String sql, Integer top, Class<T> tClass, Object... params);


    /**
     * 根据条件查询多条记录: 例（and a.name = ?）
     */
    <T> List<T> findList(Class<T> t, UnaryOperator<WhereCondition<T>> func);

    <T> T find(Class<T> t, UnaryOperator<WhereCondition<T>> func);

    <T> List<T> findTop(Class<T> tClass, Integer topNum, UnaryOperator<WhereCondition<T>> func);


    /**
     * 根据多个主键查询多条记录
     */
    <T> List<T> findAllById(Class<T> t, Collection<?> keys);

    <T, R> List<R> findIdExists(Class<T> t, Collection<R> keys);


    /**
     * 根据主键查询一条记录
     */
    <T> T findById(Class<T> t, Serializable key);


    /************************************************  条件构造器  *****************************************************/

    /**
     * 条件构造器查询-分页查询
     */
    <T> PageResult<T> queryPage(NativeSqlQuery nativeSqlQuery, Class<T> tClass, PageParam page);

    <T> PageResult<T> queryPage(NativeSqlQuery nativeSqlQuery, RowMapper<T> rowMapper, PageParam page);

    <T> PageResult<T> queryPage(String sqlStr, RowMapper<T> rowMapper, PageParam page, Object... params);

    <T> PageResult<T> queryPage(String sql, Class<T> tClass, PageParam page, Object... params);

    /**
     * 条件构造器查询-查询多个
     */
    <T> List<T> findList(NativeSqlQuery nativeSqlQuery, Class<T> tClass);


    /**
     * 条件构造器查询-查询单个对象
     */
    <T> T find(NativeSqlQuery nativeSqlQuery, Class<T> tClass);

    /**
     * 条件构造器查询-查询数量
     */
    <T> long count(NativeSqlQuery nativeSqlQuery);


    /**
     * 查询单条记录，!= null 的实体属性即为条件(全等查询)
     */
    <T> T query(T entity);

    /**
     * 查询多条记录，!= null 的实体属性即为条件(全等查询)
     */
    <T> List<T> findList(T entity);




    /* ----------------------------------------------------------------delete---------------------------------------------------------------- */

    /**
     * 根据主键删除一条记录
     */
    <T> int deleteById(Class<T> t, Serializable key);

    /**
     * 根据主键删除多条记录
     */
    <T> int deleteAllById(Class<T> t, Iterable<? extends Serializable> keys);

    <T> void delete(T entity);

    /**
     * 根据条件删除记录
     */
    <T> int deleteByCondition(Class<T> t, UnaryOperator<WhereCondition<T>> func);


    /* ----------------------------------------------------------------insert---------------------------------------------------------------- */

    /**
     * 插入数据库,如果是自增主键就返回主键值，否者返回null
     *
     * @param sql    sql语句
     * @param params sql参数
     * @return 成功则返回主键值，失败则抛出DbException
     */
    @Nullable
    public Object insert(String sql, Object... params);

    /**
     * 插入一条记录(默认在实体中set新的主键)
     */
    <T> Object insert(T entity);

    <T> Object insertIgnoreNull(T entity);

    <T> void insertAll(List<T> dataList);


    /* ----------------------------------------------------------------update---------------------------------------------------------------- */

    /**
     * 执行sql命令, 失败则抛出DbException
     *
     * @param sql sql语句
     */
    public int execute(String sql);

    public int execute(String sql, Object[] params) ;

    <T> void updateAll(List<T> dataList);



    int[] batchUpdate(String sql, BatchPreparedStatementSetter pss);

    /**
     * Execute a batch using the supplied SQL statement with the batch of supplied arguments.
     * @param sql the SQL statement to execute
     * @param batchArgs the List of Object arrays containing the batch of arguments for the query
     * @return an array containing the numbers of rows affected by each update in the batch
     * (may also contain special JDBC-defined negative values for affected rows such as
     * {@link java.sql.Statement#SUCCESS_NO_INFO}/{@link java.sql.Statement#EXECUTE_FAILED})
     */
    int[] batchUpdate(String sql, List<Object[]> batchArgs);

    /**
     * Execute a batch using the supplied SQL statement with the batch of supplied arguments.
     * @param sql the SQL statement to execute.
     * @param batchArgs the List of Object arrays containing the batch of arguments for the query
     * @param argTypes the SQL types of the arguments
     * (constants from {@code java.sql.Types})
     * @return an array containing the numbers of rows affected by each update in the batch
     * (may also contain special JDBC-defined negative values for affected rows such as
     * {@link java.sql.Statement#SUCCESS_NO_INFO}/{@link java.sql.Statement#EXECUTE_FAILED})
     */
    int[] batchUpdate(String sql, List<Object[]> batchArgs, int[] argTypes);

    /**
     * 通过where条件更新，更新字段放在entity中
     *
     * @param entity 更新字段内容
     * @param func   where参数
     * @param <T>    entity class
     * @return
     */
    <T> int updateByCondition(T entity, UnaryOperator<WhereCondition<T>> func);

    /**
     * 更新记录
     *
     * @param entity
     * @param <T>
     */
    <T> void update(T entity);

    /**
     * 保存忽略空值
     *
     * @param entity
     * @param <T>
     */
    <T> void updateIgnoreNull(T entity);


    /* ----------------------------------------------------------------common---------------------------------------------------------------- */

    /**
     * 保存一条记录（根据主键添加或修改）
     */
    <T> Object save(T entity);

    <T> Object saveIgnoreNull(T entity);

    /**
     * Batch update, first execute the update operation, then the insert operation
     * <p>
     * it is not performed in the transaction by default
     * if you want to perform with transaction ,you should call {@link #startTransaction()} before batch save
     * and call {@link #commit()} after batch update finished, and do not forget to close the sql connection!!!!!
     * <p>
     * 批量更新，先执行更新操作,后执行插入操作 默认不支持事务
     * 如果需要在事务中执行，请手动显示调用开启事务，批量保存结束后手动提交事务 并关闭 sql connection!!!!!!!!!
     * <p>
     * The pojo if extends {@link PersistEntity},and it will explicitly specify whether it is new by the field isNew
     * 实体如果继承{@link PersistEntity} 通过isNew字段来表示是否是新建还是更新，默认是新增
     * <p>
     * 1.对于mysql主键自增这种实体通过重写{@link PersistEntity#isNew()}判断主键是否为null就可以标识是否新增
     * 2.对于通过序列来设置主键值的情形,在构造dataList时,需要显示调用{@link  PersistEntity#setNew(boolean)} 来标识是否新增
     * <p>
     * If the pojo is normal entity, then will  check if the record is already persisted at the database by the primary key
     * 实体如果是普通Pojo,对主键有值的对象会判断是否存在数据库中,进而判断是否为更新或插入操作
     *
     * @param dataList 数据
     * @param <T>      类型
     */
    <T> void saveAll(List<T> dataList);


    /**
     * 删除表
     */
    void dropTables(Class<?>... arr) throws SQLFeatureNotSupportedException;

    /**
     * 创建表
     */
    void createTables(Class<?>... arr) throws SQLFeatureNotSupportedException;

}
